package com.gitee.fastmybatis.core.ext.jpa;

import com.gitee.fastmybatis.core.ext.code.util.FieldUtil;
import com.gitee.fastmybatis.core.query.Joint;
import com.gitee.fastmybatis.core.util.StringUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * @author tanghc
 */
public class ConditionUtil {

    public static final String FIND_BY_PREFIX = "findBy";

    private static final String SPLIT_AND = "_and_";
    private static final String SPLIT_OR = "_or_";
    private static final String SPLIT_ORDER_BY = "_order_by_";

    private static final String DESC = "DESC";
    private static final String ASC = "ASC";
    private static final String ORDER_BY = "ORDER BY";


    public static ConditionDefinition getConditions(String methodName) {
        // NameLikeAndAgeLessThanOrGender
        String expression = methodName.substring(FIND_BY_PREFIX.length());
        // name_like_and_age_less_than_or_gender
        String wordsContent = FieldUtil.camelToUnderline(FieldUtil.lowerFirstLetter(expression));
        List<ConditionWrapper> conditionWrappers = findConditions(wordsContent);
        String orderBy = buildOrderBy(wordsContent);
        if (StringUtil.hasText(orderBy) && !orderBy.startsWith(ORDER_BY)) {
            orderBy = ORDER_BY + " " + orderBy;
        }
        return new ConditionDefinition(conditionWrappers, orderBy);
    }

    public static String buildOrderBy(String wordsContent) {
        // name_like_and_age_less_than_or_gender_order_by_username_desc
        // name_like_and_age_less_than_or_gender_order_by_username_asc
        // name_like_and_age_less_than_or_gender_order_by_username
        if (wordsContent.endsWith("_order_by")) {
            throw new JpaQueryMethodException("OrderBy未指定列名，如：findByAgeOrderByLastnameDesc");
        }
        String find = "_order_by_";
        int index = wordsContent.indexOf(find);
        if (index == -1) {
            return "";
        }
        // username_desc
        String expression = wordsContent.substring(index + find.length());
        // 单字段，默认asc
        if (!expression.contains(JpaKeyword.desc.getKeyword()) && !expression.contains(JpaKeyword.asc.getKeyword())) {
            return expression + " ASC";
        }
        List<String> orderByContent = new ArrayList<>();
        // username_desc_age_asc_add_time_desc
        while (true) {
            if ("".equals(expression)) {
                break;
            }
            String sort;
            int idx;
            int idxDesc = expression.indexOf("_desc");
            int idxAsc = expression.indexOf("_asc");
            if (idxDesc != -1 && idxAsc != -1) {
                if (idxDesc < idxAsc) {
                    idx = idxDesc;
                    sort = DESC;
                } else {
                    idx = idxAsc;
                    sort = ASC;
                }
            } else {
                if (idxDesc > idxAsc) {
                    idx = idxDesc;
                    sort = DESC;
                } else {
                    idx = idxAsc;
                    sort = ASC;
                }
            }
            if (idx == -1) {
                sort = ASC;
                idx = expression.indexOf("_asc");
                if (idx == -1) {
                    // do last one
                    orderByContent.add(expression + " " + sort);
                    break;
                }
            }
            String name = expression.substring(0, idx);
            orderByContent.add(name + " " + sort);
            expression = expression.substring(idx + sort.length() + 1);
            expression = StringUtil.trimLeadingCharacter(expression, '_');
        }
        return String.join(",", orderByContent);
    }

    /**
     * 方法名称转条件
     * <pre>
     *     name_like_and_age_less_than_or_gender_order_by_username_desc -> and name like ? and age < ? or gender = ? order by username desc
     * </pre>
     *
     * @param wordsContent name_like_and_age_less_than_or_gender_order_by_username_desc
     * @return 返回条件，不包含order by
     * @see <a href="https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods">JPA</a>
     */
    private static List<ConditionWrapper> findConditions(String wordsContent) {
        int orderByIndex = wordsContent.indexOf(SPLIT_ORDER_BY);
        if (orderByIndex > -1) {
            wordsContent = wordsContent.substring(0, orderByIndex);
        }
        List<ConditionWrapper> conditions = new ArrayList<>(4);
        Joint currentJoint = Joint.AND;
        while (true) {
            Joint nextJoint;
            int idxAnd = wordsContent.indexOf(SPLIT_AND);
            int idxOr = wordsContent.indexOf(SPLIT_OR);
            int index;
            if (idxAnd != -1 && idxOr != -1) {
                if (idxAnd < idxOr) {
                    index = idxAnd;
                    nextJoint = Joint.AND;
                } else {
                    index = idxOr;
                    nextJoint = Joint.OR;
                }
            } else {
                if (idxAnd > idxOr) {
                    index = idxAnd;
                    nextJoint = Joint.AND;
                } else {
                    index = idxOr;
                    nextJoint = Joint.OR;
                }
            }
            if (index == -1) {
                conditions.add(buildConditionWrapper(currentJoint, wordsContent));
                break;
            }

            // name
            // name_like
            String expression = wordsContent.substring(0, index);
            conditions.add(buildConditionWrapper(currentJoint, expression));
            wordsContent = wordsContent.substring(index + nextJoint.getJoint().length() + 2);
            currentJoint = nextJoint;
        }
        return conditions;
    }

    private static ConditionWrapper buildConditionWrapper(Joint currentJoint, String expression) {
        JpaKeyword keyword = findKeyword(expression);
        String column = expression;
        if (expression.endsWith(keyword.getKeyword())) {
            column = expression.substring(0, expression.length() - keyword.getKeyword().length());
        }
        return new ConditionWrapper(column, keyword, currentJoint.getJoint());
    }

    private static JpaKeyword findKeyword(String expression) {
        //  优先处理
        if (expression.endsWith(JpaKeyword.not_in.getKeyword())) {
            return JpaKeyword.not_in;
        }
        if (expression.endsWith(JpaKeyword.is_not_null.getKeyword())) {
            return JpaKeyword.is_not_null;
        }
        if (expression.endsWith(JpaKeyword.not_null.getKeyword())) {
            return JpaKeyword.not_null;
        }
        if (expression.endsWith(JpaKeyword.not_like.getKeyword())) {
            return JpaKeyword.not_like;
        }
        for (JpaKeyword value : JpaKeyword.values()) {
            if (expression.endsWith(value.getKeyword())) {
                return value;
            }
        }
        return JpaKeyword.equals;
    }


}
